using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class DictCounter<TKey>
{
  protected class Entry
  {
    public TKey key;
    public int curCount;
    public int maxCount;

    public EntryInfo ToInfo() {
      return EntryInfo.Create(key, curCount, maxCount);
    }
  }

  public struct EntryInfo
  {
    public TKey key {
      get {
        if (!m_valid)
        {
          Debug.LogError("acess invalid entry");
        }
        return m_key;
      }
    }
    public int curCount {
      get {
        if (!m_valid)
        {
          Debug.LogError("acess invalid entry");
        }
        return m_curCount;
      }
    }
    public int maxCount {
      get {
        if (!m_valid)
        {
          Debug.LogError("access invalid entry");
        }
        return m_maxCount;
      }
    }

    private TKey m_key;
    private int m_curCount;
    private int m_maxCount;

    private bool m_valid;

    public bool isInvalid => m_valid;

    public static EntryInfo INVALID => new EntryInfo {
      m_valid = false
    };

    public static EntryInfo Create(TKey key, int curCount, int maxCount) {
      var info = new EntryInfo();
      info.m_valid = true;
      info.m_maxCount = maxCount;
      info.m_curCount = curCount;
      info.m_key = key;
      return info;
    }
  }

  private Dictionary<TKey, Entry> m_modelDict = new Dictionary<TKey, Entry>();

  public void RegisterEntry(TKey key) {
    if (!m_modelDict.ContainsKey(key))
    {
      m_modelDict[key] = new Entry() {
        key = key
      };
    }
  }

  public EntryInfo GetEntryInfo(TKey key) {
    if (m_modelDict.ContainsKey(key))
    {
      return m_modelDict[key].ToInfo();
    }
    return EntryInfo.INVALID;
  }

  public IEnumerable<EntryInfo> GetAllEntrys() {
    yield break;
  }

  public void ResetToMax(TKey key) {
    if (!m_modelDict.ContainsKey(key))
    {
      Debug.LogError("[ResetToMax] id no exist: " + key);
      return;
    }
    var entry = m_modelDict[key];
    entry.curCount = entry.maxCount;
  }

  public void UpdateEntryMax(TKey key, int maxCount) {
    if (!m_modelDict.ContainsKey(key))
    {
      Debug.LogError("[UpdateEntryMax] id no exist: " + key);
      return;
    }
    var entry = m_modelDict[key];
    maxCount = Mathf.Max(0, maxCount);
    entry.maxCount = maxCount;
  }

  public void UpdateEntryCurrent(TKey key, int curCount) {
    if (!m_modelDict.ContainsKey(key))
    {
      Debug.LogError("[UpdateEntryCurrent] id no exist: " + key);
      return;
    }
    var entry = m_modelDict[key];
    curCount = Mathf.Clamp(curCount, 0, entry.maxCount);
    entry.curCount = curCount;
  }

  public void UpdateEntry(TKey key, int curCount, int maxCount) {
    if (!m_modelDict.ContainsKey(key))
    {
      Debug.LogError("[UpdateEntry] id no exist: " + key);
      return;
    }
    var entry = m_modelDict[key];
    maxCount = Mathf.Max(0, maxCount);
    curCount = Mathf.Clamp(curCount, 0, maxCount);
    entry.curCount = curCount;
    entry.maxCount = maxCount;
  }


  public bool IsEmpty() {
    foreach (var ball in m_modelDict.Values)
    {
      if (ball.curCount > 0)
      {
        return true;
      }
    }
    return false;
  }

  /// <summary>
  /// 尝试扣除指定的球
  /// </summary>
  /// <param name="count">希望扣除的数量</param>
  /// <returns>未能成功扣除的数量，0等于正好扣除，大于0则有不足以扣除的情况，-1为不存在该球</returns>
  public int TryConsume(TKey key, int count) {
    if (m_modelDict.ContainsKey(key))
    {
      if (m_modelDict[key].curCount >= count)
      {
        m_modelDict[key].curCount -= count;
        return 0;
      } else
      {
        var cur = m_modelDict[key].curCount;
        m_modelDict[key].curCount = 0;
        return count - cur;
      }
    }
    return -1;
  }

  public EntryInfo GetNotEmptyEntryRandomly() {
    var notEmptyList = new List<Entry>();
    foreach (var entry in m_modelDict.Values)
    {
      if (entry.curCount > 0)
      {
        notEmptyList.Add(entry);
      }
    }
    if (notEmptyList.Count > 0)
    {
      return notEmptyList[Random.Range(0, notEmptyList.Count)].ToInfo();
    } else
    {
      //没有球的时候返回null
      return EntryInfo.INVALID;
    }
  }
}
